package com.yh.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Properties;

/**
 * jdbc工具类最终版本
 * 1、获取连接关闭连接方法(使用druid连接池完成)
 * 2、dml语句执行方法(传入sql以及占位符数据执行完毕返回结果)
 * 3、dql语句执行方法(基于orm对象关系映射 执行sql返回对应的集合数据)
 */
public class MyJdbcUtil {

    //无需保存额外的配置  只需要保存连接池对象即可
    private static   DataSource dataSource;

    //在静态代码块中 创建连接池对象
    static {
        ClassLoader classLoader = MyJdbcUtil.class.getClassLoader();
        //类加载器方法 会指定加载src下指定配置文件获取输入流
        InputStream is = classLoader.getResourceAsStream("druid.properties");
        Properties p=new Properties();
        try {
            p.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    //获取连接方法
    public static Connection getCon(){
        Connection connection=null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return connection;
    }

    
    //dml方法
    //可以帮我们实现所有dml语句的执行
    public static int dml(String sql,Object ...obj){
        //获取连接
        Connection con = getCon();
        PreparedStatement ps=null;
        int count=0;//影响条数
        try {
            //获取sql预编译对象
            ps= con.prepareStatement(sql);
            //将占位符替换
            for (int i = 0; i < obj.length; i++) {
                ps.setObject(i+1,obj[i]);
            }
            //执行sql
            count=ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            MyJdbcUtil.close(ps,con);
        }
        return count;
    }
    
    
    
    

    //关闭方法
    public static void close(ResultSet rs, Statement s,Connection con){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(s!=null){
            try {
                s.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    //关闭方法
    public static void close( Statement s,Connection con){
        if(s!=null){
            try {
                s.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }


    /**
     * dql语句查询方法
     * @param c 传入orm映射对象的class对象 并且
     * @param sql 执行的sql语句 参数使用?占位符替换
     * @param obj 对应占位符传入的数据 必须与?占位符匹配
     * @param <E> 当前sql处理后返回的集合中的数据类型
     * @return 所有对应类型数据的集合
     */
    public static<E> ArrayList<E> dql(Class<E> c,String sql,Object ...obj){
        ArrayList<E> list=new ArrayList<>();
        Connection con = getCon();
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            ps = con.prepareStatement(sql);
            for (int i = 0; i < obj.length; i++) {
                ps.setObject(i+1,obj[i]);
            }
            rs = ps.executeQuery();
            while(rs.next()){
                //获取传入类型的无参构造方法
                Constructor<E> declaredConstructor = c.getDeclaredConstructor();
                //调用创建对象
                declaredConstructor.setAccessible(true);
                E e = declaredConstructor.newInstance();
                //基于ORM对象关系映射 默认情况下我们要求 列名与属性名相同
                //获取所有的属性
                Field[] declaredFields = c.getDeclaredFields();
                //遍历属性
                for (Field declaredField : declaredFields) {
                    String fieldName=declaredField.getName();
                    //获取当前对应列的值
                    Object fieldValue= rs.getObject(fieldName);

                    declaredField.setAccessible(true);
                    //为对应对象 对应属性赋值
                    declaredField.set(e,fieldValue);
                    //System.out.print(fieldValue+" ");
                }
                //System.out.println();
                list.add(e);
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }finally {
            close(rs,ps,con);
        }
        return list;
    }


}
